import { _decorator, Component, Node, Enum, UITransform, Widget, NodeEventType, math } from 'cc';
const { ccclass, property, requireComponent } = _decorator;

export enum VerticalConstraint {
    align_top,
    align_bottom,
}
Enum(VerticalConstraint)

export enum HorizontalConstraint {
    align_left,
    align_right,
}
Enum(HorizontalConstraint)

/**
 * 约束布局
 */
@ccclass('ConstraintComponent')
@requireComponent(Widget)
export class ConstraintComponent extends Component {

    /** 约束 Top */
    @property({
        type: Node,
        group: 'top',
    })
    topNode: Node | null = null;

    @property({
        type: VerticalConstraint,
        tooltip: "相对TopNode的对齐方式",
        group: 'top',
    })
    topConstraint: VerticalConstraint = VerticalConstraint.align_bottom;

    @property({
        tooltip: "相对TopNode的边距",
        group: 'top',
    })
    topMargin: number = 0;

    @property({
        tooltip: "边距的单位 true:%, false=px",
        group: 'top',
    })
    topRatio: boolean = false;

    /** 约束 Bottom */
    @property({
        type: Node,
        group: 'bottom',
    })
    bottomNode: Node | null = null;

    @property({
        type: VerticalConstraint,
        tooltip: "相对BottomNode的对齐方式",
        group: 'bottom',
    })
    bottomConstraint: VerticalConstraint = VerticalConstraint.align_top;

    @property({
        tooltip: "相对BottomNode的边距",
        group: 'bottom',
    })
    bottomMargin: number = 0;

    @property({
        tooltip: "边距的单位 true:%, false=px",
        group: 'bottom',
    })
    bottomRatio: boolean = false;

    /** 约束 Left */
    @property({
        type: Node,
        group: 'left',
    })
    leftNode: Node | null = null;

    @property({
        type: HorizontalConstraint,
        tooltip: "相对LeftNode的对齐方式",
        group: 'left',
    })
    leftConstraint: HorizontalConstraint = HorizontalConstraint.align_right

    @property({
        tooltip: "相对LeftNode的边距",
        group: 'left',
    })
    leftMargin: number = 0;

    @property({
        tooltip: "边距的单位 true:%, false=px",
        group: 'left',
    })
    leftRatio: boolean = false;

    /** 约束 Right */
    @property({
        type: Node,
        group: 'right',
    })
    rightNode: Node | null = null;

    @property({
        type: HorizontalConstraint,
        tooltip: "相对RightNode的对齐方式",
        group: 'right',
    })
    rightConstraint: HorizontalConstraint = HorizontalConstraint.align_left;


    @property({
        tooltip: "相对LeftNode的边距",
        group: 'right',
    })
    rightMargin: number = 0;

    @property({
        tooltip: "边距的单位 true:%, false=px",
        group: 'right',
    })
    rightRatio: boolean = false;

    private init() {
        if (this.topNode) {
            if (this.topNode.parent !== this.node.parent) {
                throw 'Constraint node need same parent';
            }
            this.topNode.on(NodeEventType.TRANSFORM_CHANGED, this.topUpdate, this);
            this.topNode.on(NodeEventType.SIZE_CHANGED, this.topUpdate, this);
        }

        if (this.bottomNode) {
            if (this.bottomNode.parent !== this.node.parent) {
                throw 'Constraint node need same parent';
            }
            this.bottomNode.on(NodeEventType.TRANSFORM_CHANGED, this.bottomUpdate, this);
            this.bottomNode.on(NodeEventType.SIZE_CHANGED, this.bottomUpdate, this);
        }

        if (this.leftNode) {
            if (this.leftNode.parent !== this.node.parent) {
                throw 'Constraint node need same parent';
            }
            this.leftNode.on(NodeEventType.TRANSFORM_CHANGED, this.leftUpdate, this);
            this.leftNode.on(NodeEventType.SIZE_CHANGED, this.leftUpdate, this);
        }

        if (this.rightNode) {
            if (this.rightNode.parent !== this.node.parent) {
                throw 'Constraint node need same parent';
            }
            this.rightNode.on(NodeEventType.TRANSFORM_CHANGED, this.rightUpdate, this);
            this.rightNode.on(NodeEventType.SIZE_CHANGED, this.rightUpdate, this);
        }
    }

    getBorder(node: Node) {
        const x = node.x;
        const y = node.y;
        let left = x
        let right = x
        let top = y
        let bottom = y
        let width = 0
        let height = 0
        const uiTransform = node.getComponent(UITransform);
        if (uiTransform) {
            width = uiTransform.width;
            height = uiTransform.height;
            const anchorX = uiTransform.anchorX;
            const anchorY = uiTransform.anchorY;
            left = x - width * anchorX;
            right = left + width;
            bottom = y - height * anchorY;
            top = bottom + height;
        }
        return {
            left, right, top, bottom, width, height
        }
    }

    getParentBorder(node: Node) {
        const uiTransform = node.getComponent(UITransform);
        if (uiTransform) {
            const width = uiTransform.width;
            const height = uiTransform.height;
            const width_half = width / 2;
            const height_half = height / 2;
            let top = height_half
            let right = width_half
            let bottom = -height_half
            let left = -width_half
            return {
                left, right, top, bottom, width, height
            }
        }
        throw ' Parent need UITransform'
    }

    topUpdate() {
        const parent = this.node.parent;
        const top = this.topNode;
        const widget = this.getComponent(Widget);
        if (top && parent && widget) {
            // 设置顶部对齐
            widget.isAlignTop = true;
            // 设置像素方式
            widget.isAbsoluteTop = true;
            const borderTop = this.getBorder(top);
            const borderParent = this.getParentBorder(parent);
            switch (this.topConstraint) {
                case VerticalConstraint.align_top: {
                    if (top === parent) break;
                    widget.top = borderParent.top - borderTop.top + (this.topRatio ? math.clamp(this.topMargin, -1, 1) * borderParent.height : this.topMargin);
                    break;
                }
                case VerticalConstraint.align_bottom: {
                    if (top === parent) break;
                    widget.top = borderParent.top - borderTop.bottom + (this.topRatio ? math.clamp(this.topMargin, -1, 1) * borderParent.height : this.topMargin);
                    break;
                }
            }
        }
    }

    bottomUpdate() {
        const parent = this.node.parent;
        const bottom = this.bottomNode;
        const widget = this.getComponent(Widget);
        if (bottom && parent && widget) {
            // 设置顶部对齐
            widget.isAlignBottom = true;
            // 设置像素方式
            widget.isAbsoluteBottom = true;
            const borderBottom = this.getBorder(bottom);
            const borderParent = this.getParentBorder(parent);
            switch (this.bottomConstraint) {
                case VerticalConstraint.align_top: {
                    if (bottom === parent) break;
                    widget.bottom = borderBottom.top - borderParent.bottom + (this.bottomRatio ? math.clamp(this.bottomMargin, -1, 1) * borderParent.height : this.bottomMargin);
                    break;
                }
                case VerticalConstraint.align_bottom: {
                    if (bottom === parent) break;
                    widget.bottom = borderBottom.bottom - borderParent.bottom + (this.bottomRatio ? math.clamp(this.bottomMargin, -1, 1) * borderParent.height : this.bottomMargin);
                    break;
                }
            }
        }
    }

    leftUpdate() {
        const parent = this.node.parent;
        const left = this.leftNode;
        const widget = this.getComponent(Widget);
        if (left && parent && widget) {
            // 设置顶部对齐
            widget.isAlignLeft = true;
            // 设置像素方式
            widget.isAbsoluteLeft = true;
            const borderLeft = this.getBorder(left);
            const borderParent = this.getParentBorder(parent);
            switch (this.leftConstraint) {
                case HorizontalConstraint.align_left: {
                    if (left === parent) break;
                    widget.left = borderLeft.left - borderParent.left + (this.leftRatio ? math.clamp(this.leftMargin, -1, 1) * borderParent.width : this.leftMargin);
                    break;
                }
                case HorizontalConstraint.align_right: {
                    if (left === parent) break;
                    widget.left = borderLeft.right - borderParent.left + (this.leftRatio ? math.clamp(this.leftMargin, -1, 1) * borderParent.width : this.leftMargin);
                    break;
                }
            }
        }
    }

    rightUpdate() {
        const parent = this.node.parent;
        const right = this.rightNode;
        const widget = this.getComponent(Widget);
        if (right && parent && widget) {
            // 设置顶部对齐
            widget.isAlignRight = true;
            // 设置像素方式
            widget.isAbsoluteRight = true;
            const borderRight = this.getBorder(right);
            const borderParent = this.getParentBorder(parent);
            switch (this.rightConstraint) {
                case HorizontalConstraint.align_left: {
                    if (right === parent) break;
                    widget.right = borderParent.right - borderRight.left + (this.rightRatio ? math.clamp(this.rightMargin, -1, 1) * borderParent.width : this.rightMargin);
                    break;
                }
                case HorizontalConstraint.align_right: {
                    if (right === parent) break;
                    widget.right = borderParent.right - borderRight.right + (this.rightRatio ? math.clamp(this.rightMargin, -1, 1) * borderParent.width : this.rightMargin);
                    break;
                }
            }
        }
    }

    protected onDestroy(): void {
        if (this.topNode) {
            this.topNode.off(NodeEventType.TRANSFORM_CHANGED, this.topUpdate, this);
            this.topNode.off(NodeEventType.SIZE_CHANGED, this.topUpdate, this);
        }

        if (this.bottomNode) {
            this.bottomNode.off(NodeEventType.TRANSFORM_CHANGED, this.bottomUpdate, this);
            this.bottomNode.off(NodeEventType.SIZE_CHANGED, this.bottomUpdate, this);
        }

        if (this.leftNode) {
            this.leftNode.off(NodeEventType.TRANSFORM_CHANGED, this.leftUpdate, this);
            this.leftNode.off(NodeEventType.SIZE_CHANGED, this.leftUpdate, this);
        }

        if (this.rightNode) {
            this.rightNode.off(NodeEventType.TRANSFORM_CHANGED, this.rightUpdate, this);
            this.rightNode.off(NodeEventType.SIZE_CHANGED, this.rightUpdate, this);
        }
    }

    private isUpdate = false;
    protected lateUpdate(dt: number): void {
        if (this.isUpdate) return;
        this.isUpdate = true;
        this.topUpdate();
        this.bottomUpdate();
        this.leftUpdate();
        this.rightUpdate();
        this.init()
    }
}


